package cn.iocoder.yudao.module.promotion.service.prizedraw;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectKit;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.promotion.controller.admin.prizedraw.vo.PrizeDrawNumberMemberPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.prizedraw.vo.PrizeDrawNumberMemberRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.prizedraw.vo.PrizeDrawReduceNumberRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.prizedraw.PrizeDrawNumberDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.prizedraw.PrizeDrawNumberMemberDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.prizedraw.PrizeDrawNumberMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.prizedraw.PrizeDrawNumberMemberMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.List;
import java.util.Objects;

import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.MEMBER_PRIZE_DRAW_NUMBER;
import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.PRIZE_DRAW_MEMBER_PRIZE_DRAW_NUMBER;

/**
 * 用户抽奖次数 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class PrizeDrawNumberMemberServiceImpl implements PrizeDrawNumberMemberService {

    @Resource
    private PrizeDrawNumberMemberMapper prizeDrawNumberMemberMapper;
    @Resource
    private MemberUserMapper memberUserMapper;
    @Resource
    private PrizeDrawNumberMapper prizeDrawNumberMapper;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public PageResult<PrizeDrawNumberMemberRespVO> paging(PrizeDrawNumberMemberPageReqVO dto) {
        queryMembers(dto);


        PageResult<PrizeDrawNumberMemberRespVO> paging = prizeDrawNumberMemberMapper.paging(dto);
        List<PrizeDrawNumberMemberRespVO> records = paging.getList();
        Map<Long, MemberUserDO> updateByMap = new HashMap<>();
        if (CollUtil.isNotEmpty(records)) {
            List<Long> memberIds = CollectKit.map(records, PrizeDrawNumberMemberRespVO::getUserId);
            List<MemberUserDO> updateByLust = memberUserMapper.selectList(new LambdaQueryWrapper<MemberUserDO>().in(MemberUserDO::getId, memberIds));
            if (CollUtil.isNotEmpty(updateByLust)) {
                updateByMap.putAll(updateByLust.stream().collect(Collectors.toMap(MemberUserDO::getId, Function.identity())));
            }
            if (Objects.nonNull(updateByMap) && CollUtil.isNotEmpty(updateByMap)) {
                records.forEach(record -> {
                    MemberUserDO sysBaseUserDto = updateByMap.get(record.getUserId());
                    if (Objects.nonNull(sysBaseUserDto)) {
                        record.setNickName(sysBaseUserDto.getNickname());
                        record.setRealName(sysBaseUserDto.getName());
                        record.setCellphone(sysBaseUserDto.getMobile());
                    }
                });
            }
        }

        return paging;
    }

    private void queryMembers(PrizeDrawNumberMemberPageReqVO dto) {
        if (StrUtil.isNotBlank(dto.getNickName())) {
            List<MemberUserDO> updateByLust = memberUserMapper.selectList(new LambdaQueryWrapper<MemberUserDO>().like(MemberUserDO::getNickname, dto.getNickName()));
            reMembers(dto, updateByLust);
        }

        if (StrUtil.isNotBlank(dto.getCellphone())) {
            List<MemberUserDO> updateByLust = memberUserMapper.selectList(new LambdaQueryWrapper<MemberUserDO>().like(MemberUserDO::getMobile, dto.getCellphone()));
            reMembers(dto, updateByLust);
        }
    }


    private static void reMembers(PrizeDrawNumberMemberPageReqVO dto, List<MemberUserDO> datas) {
        if (Objects.nonNull(datas) && CollUtil.isNotEmpty(datas)) {
            if (CollUtil.isNotEmpty(datas)) {
                dto.setMemberIds(CollectKit.map(datas, MemberUserDO::getId));
            } else {
                dto.setMemberIds(ListUtil.list(false, -1L));
            }
        } else {
            dto.setMemberIds(ListUtil.list(false, -1L));
        }
    }

    @Override
    public void export(PrizeDrawNumberMemberPageReqVO dto) {
        dto.setPageNo(1);
        dto.setPageSize(Integer.MAX_VALUE);

        PageResult<PrizeDrawNumberMemberRespVO> downstream = paging(dto);
        List<PrizeDrawNumberMemberRespVO> records = downstream.getList();

        HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
        List<Collection<String>> rows = CollUtil.newArrayList();
        List<String> head = CollUtil.newArrayList("用户昵称", "真实姓名", "手机号",
                "获得/消耗", "当前剩余次数", "途径", "时间");
        if (CollUtil.isNotEmpty(records)) {
            records.forEach(x -> {
                String settingType;
                switch (x.getSettingType()) {
                    case 1:
                        settingType = "龙豆兑换";
                        break;
                    case 2:
                        settingType = "邀新";
                        break;
                    case 3:
                        settingType = "连续签到";
                        break;
                    case 4:
                        settingType = "商城下单";
                        break;
                    case 5:
                        settingType = "预约试驾核销";
                        break;
                    case 6:
                        settingType = "抽奖";
                        break;
                    default:
                        settingType = "--";
                        break;
                }

                List<String> row = CollUtil.newArrayList(StrUtil.isNotBlank(x.getNickName()) ? x.getNickName() : "--",
                        StrUtil.isNotBlank(x.getRealName()) ? x.getRealName() : "--",
                        StrUtil.isNotBlank(x.getCellphone()) ? x.getCellphone() : "--",
                        Integer.valueOf(1).equals(x.getUseType()) ? "获得" : "消耗",
                        x.getDrawNumber().toString(),
                        settingType,
                        DateUtil.format(x.getCreateTime(), DatePattern.NORM_DATETIME_PATTERN));
                rows.add(row);
            });
        }

        excel(head, rows, response);
    }


    private static void excel(List<String> head, List<Collection<String>> rows, HttpServletResponse response) {
        ExcelWriter writer = ExcelUtil.getWriter(true);
        // 一次性写出内容，使用默认样式，强制输出标题
        writer.writeHeadRow(head);
        writer.write(rows, true);

        // response为HttpServletResponse对象
        if (response != null) {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        }
        // test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
        ServletOutputStream out = null;
        try {
            if (response != null) {
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(IdUtil.fastSimpleUUID() + ".xlsx", StandardCharsets.UTF_8.name()));
            }
            if (response != null) {
                out = response.getOutputStream();
            }
        } catch (IOException e) {
            log.error("导出excel异常", e);
        } finally {
            writer.flush(out, true);
            // 关闭writer，释放内存
            writer.close();
            //此处记得关闭输出Servlet流
            IoUtil.close(out);
        }
    }



    @Override
    public PrizeDrawNumberMemberDO getByMemberId(Long memberId) {
        LambdaQueryWrapper<PrizeDrawNumberMemberDO> lqw = new LambdaQueryWrapper();
        lqw.eq(PrizeDrawNumberMemberDO::getUserId, memberId);
        PrizeDrawNumberMemberDO motoPrizeDrawNumberMember = prizeDrawNumberMemberMapper.selectList(lqw).stream().findFirst().orElse(null);
        return motoPrizeDrawNumberMember;
    }



    /**
     * 获取次数
     *
     * @param userId 会员id
     * @return 次数
     */
    @Override
    public Integer getNumber(Long userId) {
        RMap<String, Object> rMap = redissonClient.getMap(PRIZE_DRAW_MEMBER_PRIZE_DRAW_NUMBER);
        Object drawNumber = rMap.get(userId.toString());
        if (Objects.nonNull(drawNumber)) {
            return Integer.parseInt(drawNumber.toString());
        } else {
            PrizeDrawNumberMemberDO member = this.getByMemberId(userId);
            if (Objects.nonNull(member)) {
                rMap.put(member.getUserId().toString(), member.getDrawNumber());
                return member.getDrawNumber();
            }
        }

        return null;
    }

    /**
     * 缓存扣减次数
     *
     * @param userId 会员id
     * @param number 次数
     */
    @Override
    public void optionNumber(Long userId, Integer number) {
        RMap<String, Object> rMap = redissonClient.getMap(MEMBER_PRIZE_DRAW_NUMBER);
        Object drawNumber = rMap.get(userId.toString());
        if (Objects.nonNull(drawNumber)) {
            int drawNumberInt = Integer.parseInt(drawNumber.toString());
            drawNumberInt = drawNumberInt + number;
            rMap.put(userId.toString(), drawNumberInt);
        }
    }

    /**
     * 减少抽奖次数
     *
     * @param numberRespVO
     */
    @Override
    public void reduceNumber(PrizeDrawReduceNumberRespVO numberRespVO) {
        prizeDrawNumberMemberMapper.reduceNumber(numberRespVO.getUserId());

        PrizeDrawNumberDO pdn = BeanUtil.copyProperties(numberRespVO, PrizeDrawNumberDO.class);
        pdn.setUseType(2);
        prizeDrawNumberMapper.insert(pdn);
    }
}